package com.roadmap.client.module.goal;

import java.util.Date;
import java.util.List;
import java.util.Vector;

import org.apache.log4j.Logger;
import org.hibernate.Hibernate;

import com.roadmap.client.module.BaseManager;
import com.roadmap.client.module.IManager;
import com.roadmap.client.module.plan.PlanManager;
import com.roadmap.common.BaseConst;
import com.roadmap.common.BaseEntity;
import com.roadmap.common.RdcLogger;
import com.roadmap.common.RdcResourceBundle;
import com.roadmap.common.BaseEnum.ActionType;
import com.roadmap.entities.Aspirant;
import com.roadmap.entities.LifeGoal;
import com.roadmap.entities.LifePlan;
import com.roadmap.entities.ext.GoalExt;
import com.roadmap.exception.ActionNotSupportedException;
import com.roadmap.exception.EntityNotFoundException;
import com.roadmap.exception.InvalidActionException;
import com.roadmap.exception.InvalidQueryException;
import com.roadmap.exception.InvalidValueException;
import com.roadmap.exception.NullObjectException;
import com.roadmap.exception.ServerException;
import com.roadmap.repo.db.RdcQuery;

public class GoalManager extends BaseManager {

	private final static Logger _log = RdcLogger.getLogger(GoalManager.class);

	public GoalManager(String aspirantId, Aspirant parent) {
		super(aspirantId, LifeGoal.class, parent);
	}

	public GoalManager(String aspirantId) {
		super(aspirantId, LifeGoal.class);
	}

	/**
	 * Check the status of the goal to see if it can be cancelled.
	 * 
	 */
	@Override
	public void cancel(BaseEntity entity) throws NullObjectException,
			InvalidActionException, InvalidValueException, ServerException,
			EntityNotFoundException, ActionNotSupportedException {
		if (checkEntityIsValid(entity) == false) {
			return;
		}

		LifeGoal goal = (LifeGoal) entity;
		if (goal.getStatus() == BaseConst.LIFEGOAL_STATUS_COMPLETED
				|| goal.getStatus() == BaseConst.LIFEGOAL_STATUS_CANCELED) {
			ActionNotSupportedException ane = new ActionNotSupportedException();
			_log.error(
					"A COMPLETED or CANCLED goal doesn't support this action.",
					ane);
			throw ane;
		}
		changeGoalStatus(entity, BaseConst.LIFEGOAL_STATUS_CANCELED);
	}

	/**
	 * To complete a goal, change its status to COMPLETED, only a UNDEGOING goal
	 * supports this action Check the status of the (N-2)th milestones should be
	 * complete
	 */
	@Override
	public void complete(BaseEntity entity) throws NullObjectException,
			InvalidActionException, InvalidValueException, ServerException,
			EntityNotFoundException, ActionNotSupportedException {
		if (checkEntityIsValid(entity) == false) {
			return;
		}

		LifeGoal goal = (LifeGoal) entity;
		if (goal.getStatus() != BaseConst.LIFEGOAL_STATUS_UNDERGOING) {
			ActionNotSupportedException ise = new ActionNotSupportedException();
			String errorMsg = "Only a UNDERGOING goal supports this action.";
			_log.error(errorMsg, ise);
			throw ise;
		}

		IManager planManager = new PlanManager(getOwnerId(), goal);
		List<BaseEntity> plans = planManager.getEntitiesOfParent();
		if (plans == null || plans.size() < 1) {
			EntityNotFoundException enf = new EntityNotFoundException();
			String errorMsg = "No plan can be found for this goal.";
			_log.error(errorMsg, enf);
			throw enf;
		}
		LifePlan plan = (LifePlan) plans.get(0);

		/*
		 * IManager stoneManager = new MilestoneManager(getOwnerId(), plan);
		 * List<BaseEntity> stones = stoneManager.getEntitiesOfParent(); if
		 * (stones == null || stones.size() < 1) { EntityNotFoundException enf =
		 * new EntityNotFoundException(); String errorMsg = "No milestone can be
		 * found for this plan."; _log.error(errorMsg, enf); throw enf; } //
		 * check if all milestones are satisfied boolean canBeCompleted = false;
		 * if (stones.size() == 1) { canBeCompleted = true; } else { Milestone
		 * stone = (Milestone) stones.get(stones.size() - 2); if
		 * (stone.getStatus() != BaseConst.MILESTONE_STATUS_SATISFIED) {
		 * ActionNotSupportedException ise = new ActionNotSupportedException();
		 * String errorMsg = "The goal can't be complete bacause not all the
		 * milestones are satisfied."; _log.error(errorMsg, ise); throw ise; }
		 * 
		 * canBeCompleted = true; }
		 */
		Date dt = new Date();
		plan.setRealEndDate(dt);
		goal.setStatus(BaseConst.LIFEGOAL_STATUS_COMPLETED);
		goal.setUpdatedTime(dt);
		Vector<BaseEntity> vec = new Vector<BaseEntity>();
		vec.add(plan);
		vec.add(goal);
		getDbUtil().saveOrUpdateEntities(vec);
		getNoticeManager().notifyForEntry(entity, ActionType.COMPLETE, null);
	}

	/**
	 * if the entity is a goal, don't really delete it from database, just
	 * change its status to DELETED, or else delete it from database
	 */
	public void delete(BaseEntity entity) throws NullObjectException,
			InvalidActionException, InvalidValueException, ServerException,
			EntityNotFoundException, ActionNotSupportedException {
		if (checkEntityIsValid(entity) == false) {
			return;
		}
		changeGoalStatus(entity, BaseConst.LIFEGOAL_STATUS_DELETED);
	}

	/**
	 * update the status of a Goal and also its updated time.
	 */
	private void changeGoalStatus(BaseEntity entity, int status)
			throws NullObjectException, InvalidActionException,
			InvalidValueException, ServerException, EntityNotFoundException {
		LifeGoal goal = (LifeGoal) entity;
		goal.setStatus(status);
		goal.setUpdatedTime(new Date());
		getDbUtil().update(goal);
	}

	/**
	 * Only a CANCLED goal supports this action. The target status should not be
	 * DELETED, CANCLED or COMPLTED.
	 * 
	 */
	public void restore(BaseEntity entity, int targetStatus)
			throws ActionNotSupportedException, InvalidValueException,
			InvalidActionException, ServerException, EntityNotFoundException {
		if (checkEntityIsValid(entity) == false) {
			return;
		}

		LifeGoal goal = (LifeGoal) entity;
		if (goal.getStatus() != BaseConst.LIFEGOAL_STATUS_CANCELED) {
			ActionNotSupportedException ane = new ActionNotSupportedException();
			_log.error("Only a CANCLED goal supports this action.", ane);
			throw ane;
		}

		if (targetStatus == BaseConst.LIFEGOAL_STATUS_COMPLETED
				|| targetStatus == BaseConst.LIFEGOAL_STATUS_CANCELED
				|| targetStatus == BaseConst.LIFEGOAL_STATUS_UNSCHEDULED) {
			ActionNotSupportedException ise = new ActionNotSupportedException();
			String errorMsg = "The goal can't be restored to the status COMPLETED or CANCLED or UNSCHEDULED.";
			_log.error(errorMsg, ise);
			throw ise;
		}
		changeGoalStatus(entity, targetStatus);
	}

	@Override
	public List<BaseEntity> getEntitiesByStatus(int[] states)
			throws ActionNotSupportedException, InvalidValueException,
			InvalidQueryException, ServerException {
		return obtainEntitiesByStatus(states);
	}

	@Override
	@Deprecated
	public List getExecutableEntities() throws ActionNotSupportedException,
			InvalidQueryException, InvalidValueException, ServerException {
		int[] states = { BaseConst.LIFEGOAL_STATUS_SCHEDULED,
				BaseConst.LIFEGOAL_STATUS_UNDERGOING };
		return obtainEntitiesByStatus(states);
	}

	@Override
	public String getEntityHealthy(BaseEntity entity)
			throws ActionNotSupportedException, InvalidValueException {
		if (!checkEntityIsValid(entity)) {
			throw new InvalidValueException();
		}

		LifeGoal goal = (LifeGoal) entity;
		int status = goal.getStatus();
		// only when status is undergoing or scheduled or unscheduled,
		// validation is needed
		if (status == BaseConst.LIFEGOAL_STATUS_UNSCHEDULED
				|| status == BaseConst.LIFEGOAL_STATUS_SCHEDULED
				|| status == BaseConst.LIFEGOAL_STATUS_UNDERGOING) {

			Date today = new Date();
			Date end = goal.getScheduledEndDate();
			if (end.before(today)) {
				return RdcResourceBundle.Entity_Symptom_Not_Ended;
			}
		}

		return RdcResourceBundle.Entity_Symptom_Healthy;
	}

	public List<BaseEntity> getEntitiesOfParent() throws NullObjectException,
			InvalidQueryException, ServerException, InvalidValueException {
		// modified on 2010-01-10
		String hql = "from LifeGoal where aspirantId=? and status<>? order by createdTime DESC";
		Object[] params = { getParent().getId(),
				BaseConst.LIFEGOAL_STATUS_DELETED };
		return (List<BaseEntity>) getDbUtil().executeHQLQuery(hql, params);
	}

	public List<BaseEntity> getEntitiesOfAspirant() throws NullObjectException,
			InvalidQueryException, ServerException, InvalidValueException {
		return getEntitiesOfParent();
	}

	@Override
	public BaseEntity get(String entityId) throws NullObjectException,
			InvalidQueryException, ServerException, InvalidValueException {
		String hql = "from LifeGoal where id=? and status<>?";
		Object[] params = { entityId, BaseConst.LIFEGOAL_STATUS_DELETED };
		return (BaseEntity) getDbUtil().getUniqueEntity(hql, params);
	}

	@Override
	public GoalExt getEntityExt(String entityId) throws NullObjectException,
			InvalidQueryException, ServerException, InvalidValueException {
		if (entityId == null) {
			NullObjectException e = new NullObjectException("entity id is NULL");
			_log.error(e);
			throw e;
		}
		String sql = "select {g.*}, g.ownerName, p.planId, p.name from (select gl.*, a.realName as ownerName from lifegoal as gl, aspirant as a where gl.goalId=:gid and gl.status<>:status and gl.aspirantId=:uid and gl.aspirantId=a.aspirantId) as g left join lifeplan as p on g.goalId=p.goalId";
		RdcQuery query = getDbUtil().createNativeSQLQuery(sql);
		query.addEntity("g", LifeGoal.class);
		query.setParameter("gid", entityId);
		query.setParameter("status", BaseConst.LIFEGOAL_STATUS_DELETED );
		query.setParameter("uid", getOwnerId());
		query.addScalar("ownerName", Hibernate.STRING);
		query.addScalar("planId", Hibernate.STRING);
		query.addScalar("name", Hibernate.STRING);
		Object[] result = (Object[]) query.getUniqueResult();
		GoalExt goal = null;
		if (result != null) {
			goal = new GoalExt((LifeGoal) result[0]);
			goal.setOwnerName((String)result[1]);
			goal.setPlanId((String)result[2]);
			goal.setPlanName((String)result[3]);
		}
		return goal;
	}

}
